
/*
 * Copyright (C) 2012 by Hao Hou<ghost89413@gmail.com>
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>*
 */

#include <event.h>
#include <ic.h>
#include <xim.h>
#include <pthread.h>
#include <ime.h>
#include <X11/Xutil.h>
#include <init.h>
#include <UI.h>
#define BUFLENGTH 64  
#define IN(l,r,x) (((l)<=x) && (x<=(r)))
#define IS_LETTER(x) (IN('a','z',x) || IN('A','Z',x))
/* 
 * As all fucntions, the event handler returns an negative number 
 * when some error occured, however, the event loop should handler
 * the failure of those handler(including user defined handler)
 * if all handler avaliable is failed, or there are no avalibable
 * handlers, the event should be forward to client window
 */
TriggerKeys trigger_keys;
TriggerKeys switch_keys;
int __check_for_hotkeys__(KeySym sym, unsigned int state, TriggerKeys* keys)
{
	int i;
	TriggerKey* p;
	state &= ~(STATE_NUM|STATE_CAPS|STATE_SCROLL);
	for(i = 0, p = keys->keys; i < keys->count; i ++, p ++)
		if(p->sym == sym && p->state == state) return 1;
	return 0;
}
int LoadFirstAvail(IMMetaData* begin, InputContext* ic, XPointer data)
{
	if(NULL == begin) return -1;
	IMMetaData* im;
	if(CreateIMIC(ic,begin) == 0) return 0; /* hmm, IC created */
	/* Failure? Try next */
	for(im = NextIM(begin); im && im != begin; im = NextIM(im))
	{
		if(CreateIMIC(ic,im) == 0) return 0;
	}
	return -1;
}
int HotKeyHandler(KeySym sym, uint32_t state, int release, InputContext* ic, XPointer data)
{
	if(!release) return -1; //All System hotkeys should be processed after key releasedd 
	if(__check_for_hotkeys__(sym,state,&trigger_keys))
	{//Trigger IME
		if(ic->Enabled)
		{
			IMPreeditEnd(xims, data);
			ic->Enabled = 0;
			FreeIMIC(ic);
			VINE_LOG_NOTICE("Input Method Swithed off");
		}
		else 
		{
			ic->Enabled = 1;
			IMMetaData* im = GetDefaultIM();
			if(LoadFirstAvail(im,ic,data) != 0)
			{
				VINE_LOG_ERROR("No Input Method loaded");
				IMPreeditEnd(xims, (XPointer)data);
				ic->Enabled = 0;
			}
			else
			{
				
				ic->PreeditBufferLength = 0;
				ic->PreeditBuffer[0] = 0;
				VINE_LOG_NOTICE("Input Method Swithed on");
			}
		}
		return 0;
	} 
	else if(__check_for_hotkeys__(sym,state,&switch_keys))
	{//Switch IME
		IMMetaData* im = NextIM(ic->CurrentIM);
		if(LoadFirstAvail(im,ic,data))
		{
			VINE_LOG_ERROR("Can not load any IM Module, disabling Input Method");
			IMPreeditEnd(xims, (XPointer)data);
			ic->Enabled = 0;
		}
		else
		{
			ic->PreeditBufferLength = 0;
			ic->PreeditBuffer[0] = 0;
			ic->Candidates.Count = 0;
			RedrawIC(ic);
			VINE_LOG_NOTICE("Input Method %s is active",im->Name);
		}
		return 0;
	}
	return -1; 
}
int AppendToPreeditText(InputContext* ic, uint32_t key)
{
	if(ic->PreeditBufferLength < PREEDIT_BUF - 1)
	{
		ic->PreeditBuffer[ic->PreeditBufferLength ++] = key;
		ic->PreeditBuffer[ic->PreeditBufferLength] = 0;
		return IME_CALL(ic->CurrentIM,OnPreeditTextChanged,
				ic->PreeditBuffer, ic->PreeditBufferLength,
				ic->IMIC);
	}
	return 0;  //anyway, the preedit buffer received the key
}
int BackspacePreeditText(InputContext* ic)
{
	if(ic->PreeditBufferLength)
	{
		ic->PreeditBuffer[--ic->PreeditBufferLength] = 0;
		return IME_CALL(ic->CurrentIM, OnPreeditTextChanged,
				ic->PreeditBuffer, ic->PreeditBufferLength,
				ic->IMIC);
	}
	return -1; //No words to delete ? So I can't handler this 
}
int EventHandler(IMForwardEventStruct* data)
{
	KeySym sym;
	XKeyEvent *event = (XKeyEvent*)&data->event;
	InputContext* ic = GetCurrentIC(data);
	ic->curent_connect = data->connect_id;
	char buffer[BUFLENGTH] = {};
	if(NULL == ic)
	{
		VINE_LOG_ERROR("Bad IC(%d)",data->icid);
		return -1;
	}
	/* Process key code */
	XLookupString(event,buffer,BUFLENGTH,&sym,NULL);
	int major = sym;                 //which key actally pressed
	int release = (event->type == KeyRelease);
	if(IS_LETTER(major))major ^= 
		(((event->state&STATE_SHIFT)?32:0)^
		((event->state&STATE_CAPS)?32:0));
	VINE_LOG_DEBUG("KeyEvent(sym=%x,st=%x,rel=%d)",sym,event->state,release);
	/* Because framework hotkey is in the highest priority, it should be processed first*/
	if(HotKeyHandler(sym, event->state, release, ic, (XPointer) data) >= 0) return 0; 
	/* Not actived ? Forward it*/
	if(ic->Enabled == 0) goto L_FORWARD;
	/* Then comes user defined handlers*/
	int rc = FE_SYSTEM;
        uint32_t id;
	KeyForwardProc user_handler = GetForwardProc(ic, sym, event->state, release, &id);
	if(user_handler) rc = user_handler(sym, event->state, id, ic->IMIC);
	if(rc == 0) return 0;
	if(FE_CLIENT == rc) goto L_FORWARD; //user handlers said, the event need client window
	/*System Default Handler*/
	rc = -1;   //return value of default handler
	if((event->state&(STATE_CTRL|STATE_ALT|STATE_SUPER)) == 0 && !release)
	{
		if((sym&0xff00) == 0)       rc = AppendToPreeditText(ic, sym);
		else if((sym&0xff) == '\b') rc = BackspacePreeditText(ic);
	}
	if(rc >= 0) return 0;   //hmmmm, it have been handered
L_FORWARD:
	//Well, forward it ..
	IMForwardEvent(xims, (XPointer) data);
	return 0;
}
